package com.hyphenate.easeui.utils;

import android.os.Environment;
import android.util.Base64;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;

/**
 * Created by 俊峰 on 2017/5/15.
 * 用于加密信息
 */

public class CryptUtil {

    public static String pass = "12345678";

    ///////////////////////////////////////////////////////////////////////////
    // DES
    ///////////////////////////////////////////////////////////////////////////

    /**
     * DES 加密算法
     * @param data 原始数据
     * @param key 密码，必须是8个字节
     * @return byte[] 经过加密之后的内容
     */
    public static byte[] desEncrypt(byte[] data, byte[] key){
        byte[] ret = null;
        if (data != null && key != null) {
            if(data.length > 0 && key.length == 8){
                // 1. 使用 Cipher 引擎 来初始化 加密，并且设置密码
                try {
                    Cipher cipher = Cipher.getInstance("DES");

                    // 1.1 DESKeySpec 用于描述DES的密码
                    DESKeySpec spec = new DESKeySpec(key);
                    // 1.2 使用 SecretKeyFactory 生成 Key对象
                    SecretKeyFactory factory = SecretKeyFactory.getInstance("DES");
                    SecretKey sk = factory.generateSecret(spec);

                    // 1.3 初始化 Cipher 为加密操作，并且指定密钥
                    cipher.init(Cipher.ENCRYPT_MODE, sk);

                    // 2. 加密数据
                    ret = cipher.doFinal(data);

                } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeySpecException | BadPaddingException | IllegalBlockSizeException | InvalidKeyException e) {
                    e.printStackTrace();
                }
            }
        }
        return ret;
    }

    /**
     * DES 解密算法
     * @param data 原始数据
     * @param key 密码，必须是8个字节
     * @return byte[] 经过解密之后的内容
     */
    public static byte[] desDecrypt(byte[] data, byte[] key){
        byte[] ret = null;
        if (data != null && key != null) {
            if(data.length > 0 && key.length == 8){
                // 1. 使用 Cipher 引擎 来初始化 解密，并且设置密码
                try {
                    Cipher cipher = Cipher.getInstance("DES");

                    // 1.1 DESKeySpec 用于描述DES的密码
                    DESKeySpec spec = new DESKeySpec(key);
                    // 1.2 使用 SecretKeyFactory 生成 Key对象
                    SecretKeyFactory factory = SecretKeyFactory.getInstance("DES");
                    SecretKey sk = factory.generateSecret(spec);

                    // 1.3 初始化 Cipher 为解密操作，并且指定密钥
                    cipher.init(Cipher.DECRYPT_MODE, sk);

                    // 2. 解密数据
                    ret = cipher.doFinal(data);

                } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeySpecException | BadPaddingException | IllegalBlockSizeException | InvalidKeyException e) {
                    e.printStackTrace();
                }
            }
        }
        return ret;
    }

    ///////////////////////////////////////////////////////////////////////////
    // RSA加密
    ///////////////////////////////////////////////////////////////////////////

    /**
     * 用于生成密钥对 公钥和私钥
     * @param bits 必须是1024或2048
     * @return
     */
    public static KeyPair generateRsaKey(int bits){
        KeyPair ret = null;
        try {
            KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
            kpg.initialize(bits);
            ret = kpg.generateKeyPair();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return ret;
    }

    /**
     * RSA加密信息
     * @param data 所需加密的信息
     * @param key 用来加密的钥匙
     * @return 加密后的信息
     */
    public static byte[] rsaEncrypt(byte[] data, Key key){
        byte[] ret = null;
        if (data != null && data.length > 0 && key != null) {
            Cipher cipher = null;
            try {
                cipher = Cipher.getInstance("RSA");
                cipher.init(Cipher.ENCRYPT_MODE, key);
                ret = cipher.doFinal(data);
            } catch (NoSuchAlgorithmException | NoSuchPaddingException | BadPaddingException | IllegalBlockSizeException | InvalidKeyException e) {
                e.printStackTrace();
            }
        }
        return ret;
    }

    /**
     * RSA解密信息
     * @param data 等待解密的信息
     * @param key 用来解密的钥匙
     * @return 解密后的信息
     */
    public static byte[] rsaDecrypt(byte[] data, Key key){
        byte[] ret = null;
        if (data != null && data.length > 0 && key != null) {
            try {
                Cipher cipher = Cipher.getInstance("RSA");
                cipher.init(Cipher.DECRYPT_MODE, key);
                ret = cipher.doFinal(data);
            } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | BadPaddingException | IllegalBlockSizeException e) {
                e.printStackTrace();
            }
        }
        return ret;
    }

    /**
     * 生成RSA的privateKey和publicKey，并保存在本地文件中
     * 保存的地址为手机的SD卡中的  MyCryptSecret 文件夹下
     * privateKey保存在该文件夹下的 privateKey.txt 文本中
     * publicKey保存在该文件夹下的 publicKey.txt 文本中
     */
    public static void saveSecret(){
        KeyPair pair = CryptUtil.generateRsaKey(1024);
        byte[] prK = pair.getPrivate().getEncoded();
        byte[] puK = pair.getPublic().getEncoded();
        //使用GZIP进行一次压缩
        ByteArrayOutputStream basPr = new ByteArrayOutputStream();
        ByteArrayOutputStream basPu = new ByteArrayOutputStream();
        try {
            GZIPOutputStream gosPr = new GZIPOutputStream(basPr);
            GZIPOutputStream gosPu = new GZIPOutputStream(basPu);
            gosPr.write(prK);
            gosPu.write(puK);
            gosPr.flush();
            gosPu.flush();
            gosPr.close();
            gosPu.close();
            byte[] bytesPr = basPr.toByteArray();
            byte[] bytesPu = basPu.toByteArray();
            //使用Base64进行一次加密
            String privateKey = Base64.encodeToString(bytesPr, Base64.NO_WRAP);
            String publicKey = Base64.encodeToString(bytesPu, Base64.NO_WRAP);
            File file = new File(Environment.getExternalStorageDirectory(), "MyCryptSecret");
            if (!file.exists()) {
                if (file.mkdir()) {
                    File privateFile = new File(file, "privateKey.txt");
                    File publicFile = new File(file, "publicKey.txt");
                    if (!privateFile.exists()) {
                        try {
                            if (privateFile.createNewFile()) {
                                FileOutputStream out = new FileOutputStream(privateFile);
                                out.write(privateKey.getBytes());
                                out.flush();
                                out.close();
                            }
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                    if (!publicFile.exists()) {
                        try {
                            if (publicFile.createNewFile()) {
                                FileOutputStream out = new FileOutputStream(publicFile);
                                out.write(publicKey.getBytes());
                                out.flush();
                                out.close();
                            }
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
            try {
                basPr.close();
                basPu.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取保存在本地的RSA的privateKey或publicKey
     * @param file  保存Key的文件
     * @param Type  要返回Key的类型，Type = 1 表示返回 privateKey，Type = 2 表示返回 publicKey
     * @return      返回privateKey或publicKey
     */
    public static Key getKeyFromFile(File file, int Type){
        Key ret = null;
        try {
            FileInputStream fis = new FileInputStream(file);
            int Length;
            byte[] buffer = new byte[1024];
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            while ((Length = fis.read(buffer)) != -1){
                bos.write(buffer, 0, Length);
            }
            String s = bos.toString();
            byte[] decode = Base64.decode(s, Base64.NO_WRAP);
            ByteArrayInputStream bis = new ByteArrayInputStream(decode);
            GZIPInputStream gzipIS = new GZIPInputStream(bis);
            ByteArrayOutputStream bosTwo = new ByteArrayOutputStream();
            while ((Length = gzipIS.read(buffer)) != -1){
                bosTwo.write(buffer, 0, Length);
            }
            byte[] bytes = bosTwo.toByteArray();
            //关闭流
            fis.close();
            bos.close();
            bis.close();
            gzipIS.close();
            bosTwo.close();
            //获取Key
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            if (Type == 1) {
                PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(bytes);
                ret = keyFactory.generatePrivate(spec);
            } else if (Type == 2) {
                X509EncodedKeySpec spec = new X509EncodedKeySpec(bytes);
                ret = keyFactory.generatePublic(spec);
            }
        } catch (IOException | NoSuchAlgorithmException | InvalidKeySpecException e) {
            e.printStackTrace();
        }
        return ret;
    }

}
